home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C ++ / Frameworks / MacZoop 1.6.5 / More Classes / File Classes / ZJPEGFile.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1997-07-18  |  17.9 KB  |  731 lines  |  [TEXT/CWIE]

  1. /*************************************************************************************************
  2. *
  3. *
  4. *            ObjectMacZapp        -- a standard Mac OOP application template
  5. *
  6. *
  7. *
  8. *            ZJPEGFile.cpp        -- a file object that can open JPEG images
  9. *
  10. *
  11. *
  12. *
  13. *
  14. *            © 1996, Graham Cox
  15. *
  16. *
  17. *
  18. *
  19. *************************************************************************************************/
  20.  
  21. #include    "ZJPEGFile.h"
  22. #include    "ZGWorld.h"
  23. #include    "MacZoop.h"
  24.  
  25. #include    <ImageCompression.h>
  26.  
  27. static             OSErr    ReadJFIFToGWorld( short refNum, GWorldPtr* theGWorld );
  28.  
  29.  
  30. /*-------------------------------------  CONSTRUCTOR ------------------------------------------*/
  31.  
  32.  
  33. ZJPEGFile::ZJPEGFile( const FSSpec& aSpec )
  34.     : ZFile( aSpec )
  35. {
  36.     FailOSErr( gMacInfo.hasImgCompressionMgr? noErr : kNoSystemSupportForFeatureErr );
  37. }
  38.  
  39.  
  40. /***********************************************************************************************
  41. *
  42. *    READ- read the JPEG file into ZGWorld object (recommended approach)
  43. *
  44. ***********************************************************************************************/
  45.  
  46. void    ZJPEGFile::Read( ZGWorld*   aGWorld )
  47. {
  48.     FailNILParam( aGWorld );
  49.     
  50.     if ( refNum == _NOT_OPEN )
  51.         FailOSErr( fnOpnErr );
  52.     
  53.     GWorldPtr    gw = aGWorld->GetMacGWorld();
  54.     
  55.     if ( gw )
  56.         DisposeGWorld( gw );
  57.         
  58.     aGWorld->SetMacGWorld( gw = NULL );
  59.     
  60.     Read( &gw );
  61.     
  62.     // now we need to make the ZGWorld object handle this GWorld that was built
  63.     // by the image compression manager. WARNING! If an exception was thrown reading the file
  64.     // the GWorld, the original GWorld will have been discarded and trying to access it via
  65.     // the object will probably crash. However, normally this is called to create a new image
  66.     // from a file, so this shouldn't be a problem. Bear it in mind for other uses however.
  67.     
  68.     aGWorld->SetMacGWorld( gw );
  69. }
  70.  
  71.  
  72. /***********************************************************************************************
  73. *
  74. *    READ- read the JPEG file into a Macintosh GWorld
  75. *
  76. ***********************************************************************************************/
  77.  
  78. void    ZJPEGFile::Read( GWorldPtr* aGWorld )
  79. {
  80.     FailNILParam( aGWorld );
  81.     FailOSErr( ReadJFIFToGWorld( refNum, aGWorld ));
  82. }
  83.  
  84.  
  85. #pragma mark -
  86. #pragma mark ####  static JPEG Decoding stuff ####
  87.  
  88. // prototypes:
  89.  
  90. static pascal     OSErr    JPEGDataLoader( Ptr *dataP, long bytesNeeded, long refCon );
  91. static ImageDescriptionHandle    ScanJPEG( short originalFile, Ptr *data, OpenCPicParams *pictureHeader );
  92. static void  SwallowQuantTable( char *data );
  93. static void  SwallowHuffTable( char *data );
  94. static char *MarkerDetect( char *data, short *width, short *height, long *hRes, long *vRes, short *depth );
  95.  
  96. // switches, etc:
  97.  
  98. //#define    USE_DATA_LOADER
  99. #define    kDefaultBufferSize    (64L * 1024L)    // 64K buffer size
  100.  
  101. /*
  102.     Written by:    Mark Krueger, Apple Computer, Inc.
  103.     
  104.     The JPEG File  Interchange Format ( JFIF) is a cross platform standard file format for
  105.     storing JPEG compressed image files. This application shows you how you can easily convert
  106.     these to QuickTime PICT format, or vice-verse. 
  107.     
  108.     You can use this in your application to open JFIF files directly or put a user interface 
  109.     ( Drag and Drop would be cool ) on it to make a conversion program ). When files are to 
  110.     be used on the Mac it is best to keep them in QTPict format so they can be easily copied
  111.     and pasted, but JFIF format is useful for transfering data to other platforms that cannot
  112.     use PICT formated JPEG images. 
  113.     
  114.     NOTE: the PICT to JFIF format translator is incomplete in that it only converts PICT
  115.     files which are already in QuickTIme JPEG format and it does not handle banded JPEG
  116.     Picts ( which may be commonly created by QuickTime applications which call 
  117.     CompressPictureFile in low memory conditions or which create their own banded picts)
  118.     To fully handle these images, the individual bands would have to be converted into a 
  119.     single JPEG stream when put into JFIF format, and this code does not show you how
  120.     to do that. 
  121.  
  122. */
  123.  
  124. // static functions, mostly courtesy Apple Computer DTS:
  125.  
  126.  
  127.  
  128. static OSErr    ReadJFIFToGWorld( short refNum, GWorldPtr* theGWorld )
  129. {
  130.     // simpler and more robust function for reading JFIF images. Because this does not go through the step of
  131.     // decompressing the image to a picture, it requires less memory than the above function. It resizes the
  132.     // GWorld to the size of the image. This may alter the GWorld ptr value, so be sure to copy it back to any
  133.     // data structures that have reference to it- e.g. layers. 
  134.  
  135.     OSErr                    result = noErr;
  136.     OpenCPicParams            header;
  137.     Ptr                        data = NULL;
  138.     Handle                    tempBuffer = NULL;
  139.     ImageDescriptionHandle    desc = NULL;
  140.     GWorldFlags                flags;
  141.     PixMapHandle            ppix;
  142.     CGrafPtr                savePort;
  143.     GDHandle                saveDevice;
  144.     ICMProgressProcRecord    iProgRec = {NULL,0};
  145.     ICMDataProcRecord        dLoadProc = {NULL,0};
  146.     
  147.     #ifdef USE_DATA_LOADER
  148.         if ((desc = ScanJPEG(refNum,NULL,&header)) == NULL)
  149.         {
  150.             result = paramErr;
  151.             goto done;
  152.         }
  153.         (*desc)->dataSize = 0;
  154.     #else
  155.         if ((desc = ScanJPEG(refNum,&data,&header)) == NULL)
  156.         {
  157.             result = paramErr;
  158.             goto done;
  159.         }
  160.     #endif
  161.     // we have a description of the image. Now resize the GWorld and set its depth. Note that we do not
  162.     // call UpdateGWorld, but simply get rid of the existing one and reallocate it. This requires half the
  163.     // memory it otherwise would because no copy is required. This is ok because we are not attempting to keep
  164.     // the existing image anyway.
  165.     
  166.     if (*theGWorld)
  167.         DisposeGWorld(*theGWorld);
  168.     
  169.     result = NewImageGWorld(theGWorld,desc,0);
  170.     
  171.     if (result)
  172.         goto done;
  173.         
  174.     // we succeeded in changing the GWorld to the size we desire, now we simply decompress the data into
  175.     // the GWorld's pixmap. We load the JPEG data using a small buffer to save memory, so set this up here
  176.     
  177.     #ifdef USE_DATA_LOADER
  178.         dLoadProc.dataProc = NewICMDataProc((ProcPtr) JPEGDataLoader);
  179.         dLoadProc.dataRefCon = refNum;
  180.         result = SetFPos(refNum,fsFromStart,0);
  181.  
  182.         tempBuffer = NewHandleClear(kDefaultBufferSize);
  183.         if (tempBuffer == NULL)
  184.         {
  185.             result = memFullErr;
  186.             goto done;
  187.         }
  188.         else
  189.         {
  190.             MoveHHi(tempBuffer);
  191.             HLock(tempBuffer);
  192.         
  193.             data = StripAddress(*tempBuffer);
  194.         }
  195.     #endif
  196.  
  197.     //iProgRec.progressProc = NewICMProgressProc((ProcPtr) StdDecompressionProgressProc);
  198.     //iProgRec.progressRefCon = 'open';    
  199.     
  200.     if (LockPixels(ppix = GetGWorldPixMap(*theGWorld)))
  201.     {
  202.         GetGWorld(&savePort,&saveDevice);    
  203.         SetGWorld(*theGWorld,NULL);
  204.         
  205.     #ifdef USE_DATA_LOADER
  206.         result = FDecompressImage(  data,desc,ppix,
  207.                                     NULL,NULL,srcCopy + ditherCopy,(RgnHandle) NULL,
  208.                                     NULL,(Rect *) NULL,codecHighQuality,anyCodec,kDefaultBufferSize,
  209.                                     (ICMDataProcRecordPtr) &dLoadProc,
  210.                                     (ICMProgressProcRecordPtr) &iProgRec);
  211.     #else
  212.         result = FDecompressImage(  data,desc,ppix,
  213.                                     NULL,NULL,srcCopy + ditherCopy,(RgnHandle) NULL,
  214.                                     NULL,(Rect *) NULL,codecHighQuality,anyCodec,0,
  215.                                     (ICMDataProcRecordPtr) NULL,
  216.                                     (ICMProgressProcRecordPtr) &iProgRec);
  217.     #endif
  218.         SetGWorld(savePort,saveDevice);
  219.         UnlockPixels(ppix);
  220.     }
  221.     else
  222.         result = -50;
  223. done:
  224.     if (tempBuffer)
  225.     {
  226.         HUnlock(tempBuffer);
  227.         DisposeHandle(tempBuffer);
  228.     }
  229.     if (desc) 
  230.         DisposeHandle((Handle) desc);
  231.     if (iProgRec.progressProc)
  232.         DisposeRoutineDescriptor(iProgRec.progressProc);
  233.     if (dLoadProc.dataProc)
  234.         DisposeRoutineDescriptor(dLoadProc.dataProc);
  235.     return result;
  236. }
  237.  
  238.  
  239.  
  240. static pascal OSErr    JPEGDataLoader(Ptr *dataP,long bytesNeeded,long refCon)
  241. {
  242.     OSErr    theErr = noErr;
  243.     short    fileRefNum = LoWord(refCon);
  244.     
  245.     if (dataP)
  246.         theErr = FSRead(fileRefNum,&bytesNeeded,*dataP);
  247.     else
  248.         theErr = SetFPos(fileRefNum,fsFromMark,bytesNeeded);
  249.     return theErr;
  250. }
  251.  
  252.  
  253. /************************************************
  254.  
  255.     Scan a file for valid JPEG data, and fill in a picture header and ImageDescription
  256.     for it.
  257.  
  258. *************************************************/
  259.  
  260. static ImageDescriptionHandle    ScanJPEG(short originalFile,Ptr *data,OpenCPicParams *pictureHeader)
  261. {
  262.     short                     w,h;
  263.     ImageDescriptionHandle     desc;
  264.     long                    l;
  265.     char                    *bitStream,*scanData,*buffer;
  266.     long                    hRes = 72L<<16,vRes = 72L<<16;
  267.     short                    depth = 32;
  268.     
  269.     // this creates a buffer which holds the whole image. This is not very efficient. Instead we will spool
  270.     // the data in using a data loading procedure.
  271.     
  272.     
  273.     GetEOF(originalFile,&l);
  274.     if ((buffer = NewPtr(l)) == nil)
  275.         return(nil);
  276.  
  277.     FSRead(originalFile,&l,buffer);
  278.     bitStream = buffer;
  279.     
  280.     if ( (desc = (ImageDescriptionHandle) NewHandle(sizeof(ImageDescription))) == nil )
  281.         return(nil);
  282.  
  283.     if ( (scanData = MarkerDetect(bitStream,&w,&h,&hRes,&vRes,&depth)) == 0 )
  284.         return(nil);
  285.  
  286.     (*desc)->idSize = sizeof(ImageDescription);
  287.     (*desc)->width = w;
  288.     (*desc)->height = h;
  289.     (*desc)->temporalQuality = 0;
  290.     (*desc)->spatialQuality = codecNormalQuality;
  291.     (*desc)->dataSize = l;
  292.     (*desc)->cType = 'jpeg';
  293.     (*desc)->version = 0;
  294.     (*desc)->revisionLevel = 0;
  295.     (*desc)->vendor = 0;
  296.     (*desc)->hRes = hRes;
  297.     (*desc)->vRes = vRes;
  298.     (*desc)->depth = depth;
  299.     (*desc)->clutID = -1;
  300.     BlockMoveData("\pPhoto",(*desc)->name,6);
  301.     SetRect(&pictureHeader->srcRect,0,0,w,h);
  302.     pictureHeader->version = -2;
  303.     pictureHeader->reserved1 = 0;
  304.     pictureHeader->reserved2 = 0;
  305.     pictureHeader->hRes = hRes;
  306.     pictureHeader->vRes = vRes;
  307.     
  308.     if (data == NULL)
  309.     {
  310.         // get rid of this buffer- we are going to load the image in small pieces
  311.     
  312.         DisposePtr(buffer);
  313.     }
  314.     else
  315.         *data = buffer;
  316.         
  317.     return(desc);
  318. }
  319.  
  320.  
  321.  
  322. /**********************************************************************
  323.  
  324.     JPEG specific stuff.
  325.     
  326. ***********************************************************************/
  327.  
  328. /*
  329.  
  330.     JPEG Marker code definitions.
  331.     
  332. */
  333.  
  334. #define    MARKER_PREFIX    0xff
  335. #define    MARKER_SOI    0xd8        /* start of image */
  336. #define    MARKER_SOF    0xc0        /* start of frame */
  337. #define    MARKER_DHT    0xc4        /* define Huffman table */
  338. #define    MARKER_EOI    0xd9        /* end of image */
  339. #define    MARKER_SOS    0xda        /* start of scan */
  340. #define    MARKER_DQT    0xdb        /* define quantization tables */
  341. #define    MARKER_DNL    0xdc        /* define quantization tables */
  342. #define    MARKER_DRI    0xdd        /* define Huffman table */
  343. #define    MARKER_COM    0xfe        /* comment */
  344. #define MARKER_APP0    0xe0        
  345.  
  346.  
  347. /**********************************************************************
  348.  
  349.     Read the quantization table from the JPEG bitstream.
  350.     
  351. ***********************************************************************/
  352.  
  353. static void  SwallowQuantTable(char *data)
  354. {
  355.     long    i;
  356.     long    length,pm,nm;
  357.  
  358.     length = *(short *)data;            /* read length */
  359.     length -= 2;
  360.     data += 2;
  361.     while ( length ) {
  362.         nm= *data++;                    /* read precision and number */
  363.         pm = nm>>4;    
  364.         nm &= 0xf;
  365.         length--;
  366.         if ( pm ) {
  367.             for(i=0;i<64;i++) {
  368.                 length -= 2;
  369.                 data += 2;
  370.             }
  371.         } else {
  372.             for(i=0;i<64;i++) {
  373.                 length--;
  374.                 data++;
  375.             }
  376.         }
  377.     }    
  378. }
  379.  
  380. /**********************************************************************
  381.  
  382.     Read the huffman table from the JPEG bitstream.
  383.     
  384. ***********************************************************************/
  385.  
  386. static void  SwallowHuffTable(char *data)
  387. {
  388.     short    i,tc,id;
  389.     long    length;
  390.     
  391.     unsigned char    bin[17];
  392.     unsigned char    val[256];
  393.  
  394.     bin[0] = 0;
  395.     length = *(short *)data;            /* read length */
  396.     data += 2;
  397.     length -= 2;
  398.     while ( length ) {
  399.         id=*data++;                /* read id */
  400.         length--;
  401.         if ( id != 0 && id != 1 && id != 0x10 && id != 0x11) {
  402.             return;
  403.         }
  404.         tc = 0;
  405.         for(i=0;i<16;i++) {
  406.             length--;
  407.             tc += (bin[i+1] = *data++);
  408.         }
  409.         for (i=0; i < tc; i++ ) {
  410.             length--;
  411.             val[i] = *data++;
  412.         }
  413.     }
  414. }
  415.     
  416.     
  417.     
  418. /**********************************************************************
  419.  
  420.     Scan the JPEG stream for the proper markers and fill in the image parameters
  421.     
  422.     returns nil if it cant comprehend the data, otherwise a pointer to the start
  423.     of the JPEG data.
  424.     
  425.     
  426.     It does a cursory check on the JPEG data to see if it's reasonable.
  427.     Check out the ISO JPEG spec if you really want to know what's going on here.
  428.     
  429. ***********************************************************************/
  430.  
  431. static char *MarkerDetect(char *data,short *width,short *height,long *hRes,long *vRes,short *depth)
  432. {
  433.     short    frame_field_length;
  434.     short    data_precision;
  435.     short    scan_field_length;
  436.     short    number_component,scan_components;
  437.     short    c1,hv1,q1,c2,hv2,q2,c3,hv3,q3;
  438.     short    dac_t1, dac_t2, dac_t3;
  439.     unsigned char    c;
  440.     short    qtabledefn;
  441.     short    htabledefn;
  442.     short    status;
  443.     short    length;
  444.     short    i;
  445.     
  446.     c = *data++;
  447.     qtabledefn = 0;
  448.     htabledefn = 0;
  449.     status = 0;
  450.     while (c != (unsigned char)MARKER_SOS) {
  451.         while (c != (unsigned char)MARKER_PREFIX)
  452.             c = *data++;                        /* looking for marker prefix bytes */
  453.         while (c == (unsigned char)MARKER_PREFIX)
  454.             c = *data++;                        /* (multiple?) marker prefix bytes */
  455.         if (c == 0)
  456.             continue;                                    /* 0 is never a marker code */
  457.  
  458.         if (c == (unsigned char)MARKER_SOF) {
  459.  
  460.             frame_field_length = *(short *)data;
  461.             data += 2;
  462.             data_precision = *data++;
  463.             
  464.             if ( data_precision != 8 ) { 
  465.                 status = 2;
  466.             }
  467.  
  468.             *height = *(short *)data;
  469.             data += 2;
  470.             *width = *(short *)data;
  471.             data += 2;
  472.                         
  473.             number_component = *data++;
  474.             
  475.             switch ( number_component  ) {
  476.             case 3:
  477.                 c1 = *data++;
  478.                 hv1 = *data++;
  479.                 q1 = *data++;
  480.                 c2 = *data++;
  481.                 hv2 = *data++;
  482.                 q2 = *data++;
  483.                 c3 = *data++;
  484.                 hv3 = *data++;
  485.                 q3 = *data++;
  486.                 *depth = 32;
  487.                 break;
  488.             case 1:        
  489.                 c1 = *data++;
  490.                 hv1 = *data++;
  491.                 q1 = *data++;
  492.                 *depth = 40;
  493.                 break;
  494.             default:
  495.                 status = 3;
  496.                 break;
  497.             }
  498.             continue;
  499.         }
  500.     
  501.         if (c == (unsigned char)MARKER_SOS) {
  502.             short tn;
  503.             scan_field_length = *(short *)data;
  504.             data += 2;
  505.             scan_components = *data++;
  506.             for ( i=0; i < scan_components; i++ ) {
  507.                 unsigned char cn,dac_t;
  508.                 
  509.                 cn = *data++;
  510.                 dac_t = *data++;
  511.                 if ( cn == c1 ) {
  512.                     dac_t1 = dac_t;
  513.                 } else if ( cn == c2 ) {
  514.                     dac_t2 = dac_t;
  515.                 } else if ( cn == c3 ) {
  516.                     dac_t3 = dac_t;
  517.                 } else {    
  518.                     status = 29;
  519.                     break;
  520.                 }
  521.             }
  522.             switch ( tn=(dac_t1 & 0xf) )  {
  523.             case 0:
  524.             case 1:
  525.                 break;
  526.             case 0xf:
  527.                 break;
  528.             default:
  529.                 status = 33;
  530.                 break;
  531.             }
  532.             switch (  tn=(dac_t2 & 0xf) )  {
  533.             case 0:
  534.             case 1:
  535.                 break;
  536.             case 0xf:
  537.                 break;
  538.             default:
  539.                 status = 33;
  540.                 break;
  541.             }
  542.             switch (  tn=(dac_t3 & 0xf) )  {
  543.             case 0:
  544.             case 1:
  545.                 break;
  546.             case 0xf:
  547.                 break;
  548.             default:
  549.                 status = 33;
  550.                 break;
  551.             }
  552.  
  553.  
  554.             /*  Initialize the DC tables */
  555.             
  556.             switch (  tn=dac_t1 & 0xf0 )  {
  557.             case 0:
  558.             case 0x10:
  559.                 break;
  560.             case 0xf0:
  561.                 break;
  562.             default:
  563.                 status = 34;
  564.                 break;
  565.             }
  566.             switch (  tn=dac_t2 & 0xf0 )  {
  567.             case 0:
  568.             case 0x10:
  569.                 break;
  570.             case 0xf0:
  571.                 break;
  572.             default:
  573.                 status = 34;
  574.                 break;
  575.             }
  576.             switch (  tn=dac_t3 & 0xf0 )  {
  577.             case 0:
  578.             case 0x10:
  579.                 break;
  580.             case 0xf0:
  581.                 break;
  582.             default:
  583.                 status = 34;
  584.                 break;
  585.             }
  586.             if ( *data++ != 0 )  {
  587. //                status = 18;
  588.             }
  589.             if ( *data++ != 63 )  {
  590. //                status = 19;
  591.             }
  592.             if ( *data++ != 0 ) {
  593. //                status = 20;
  594.             }
  595.             if ( status )
  596.                 return(0);
  597.             else
  598.                 return(data);
  599.         }
  600.  
  601.         if (c == (unsigned char)MARKER_DQT) {
  602.             scan_field_length = *(short *)data;
  603.             SwallowQuantTable(data);
  604.             data += scan_field_length;
  605.             continue;
  606.         }
  607.         if (c == (unsigned char)MARKER_DHT) {
  608.             scan_field_length = *(short *)data;
  609.             SwallowHuffTable(data);
  610.             continue;
  611.         }
  612.         if (c == (unsigned char)MARKER_DRI) {
  613.             length = *(short *)data;            /* read length */
  614.             data += 2;
  615.             length = *(short *)data;            
  616.             data += 2;
  617.             continue;
  618.         }
  619.         if (c == (unsigned char)MARKER_DNL) {
  620.             length = *(short *)data;            /* read length */
  621.             data += 2;
  622.             length = *(short *)data;            
  623.             data += 2;
  624.             continue;
  625.         }
  626.         if (c >= (unsigned char)0xD0 && c <= (unsigned char)0xD7) {
  627.             continue;
  628.         }
  629.  
  630.         if (c == (unsigned char)MARKER_SOI || c == (unsigned char)MARKER_EOI)    /* image start, end marker */
  631.             continue;
  632.  
  633.         if ( (c >= (unsigned char)0xC1 && c <= (unsigned char)0xcF) || (c == (unsigned char)0xde) || (c == (unsigned char)0xdf) ) {
  634.             status = 12;
  635.             length = *(short *)data;            /* read length */
  636.             data += length;
  637.             continue;
  638.         }
  639.         if (c >= (unsigned char)MARKER_APP0 && c <= (unsigned char)0xEF) {
  640.             length = *(short *)data;            /* read length */
  641.             data += 2;
  642.             length -= 2;
  643.             if ( (c == (unsigned char)MARKER_APP0) && length > 5 ) { /* check for JFIF marker */
  644.                 char buf[5];
  645.                 buf[0] = *data++;
  646.                 buf[1] = *data++;
  647.                 buf[2] = *data++;
  648.                 buf[3] = *data++;
  649.                 buf[4] = *data++;
  650.                 length -= 5;
  651.                 
  652.                 if ( buf[0] == 'J' && buf[1] == 'F'  && buf[2] == 'I'  && buf[3] == 'F' ) {
  653.                     short    units;
  654.                     long    xres,yres;
  655.                     short    version;
  656.                     
  657.                     
  658.                     version = *(short *)data; data += 2;length -= 2;
  659.                     if ( version != 0x100 && version != 0x101 && version != 0x102) {
  660.                         status = 44;        // unknown JFIF version
  661.                         break;
  662.                     }
  663.                     units = *data++; length--;
  664.                     xres = *(short *)data; data += 2; length -= 2;
  665.                     yres = *(short *)data; data += 2; length -= 2;
  666.  
  667.                     switch ( units ) {
  668.                     case 0:            // no res, just aspect ratio
  669.                         // some files will have bad res data here. In this case, xRes & yRes will
  670.                         // be zero. This really means that they should be 1! This allows some dodgy
  671.                         // JPEGs created by some crappy PC software to be opened
  672.                         
  673.                         if (xres == 0)
  674.                             xres = 1;
  675.                         if (yres == 0)
  676.                             yres = 1;
  677.                     
  678.                         *hRes = FixMul(72L<<16,xres<<16);
  679.                         *vRes = FixMul(72L<<16,yres<<16);
  680.                         break;
  681.                     case 1:            // dots per inch
  682.                         *hRes = xres<<16;
  683.                         *vRes = yres<<16;
  684.                         break;
  685.                     case 2:            // dots per centimeter (we convert to dpi )
  686.                         *hRes = FixMul(0x28a3d,xres<<16);
  687.                         *vRes = FixMul(0x28a3d,xres<<16);
  688.                         break;    
  689.                     default:
  690.                         break;
  691.                     }
  692.                     xres = *data++; length--;
  693.                     yres = *data++; length--;
  694.                     
  695.                     /* skip JFIF thumbnail */
  696.                     
  697.                     xres *= yres;
  698.                     data += xres*3; length -= xres*3;
  699.                     
  700.                     if (  length != 0 ) {
  701.                         status = 44;        // bad jfif marker
  702.                         break;
  703.                     }
  704.                 }
  705.             }
  706.             data += length;
  707.             continue;
  708.         }
  709.         if (c == (unsigned char)MARKER_COM) {
  710.             length = *(short *)data;            /* read length */
  711.             data += length;
  712.             continue;
  713.         }
  714.         if (c >= (unsigned char)0xf0 && c <= (unsigned char)0xfd) {
  715.             length = *(short *)data;            /* read length */
  716.             data += length;
  717.             continue;
  718.         }
  719.         if ( c == 0x1 )
  720.             continue;
  721.         if ( (c >= (unsigned char)0x2 && c <= (unsigned char)0xbF) ) {
  722.             length = *(short *)data;            /* read length */
  723.             status = 13;
  724.             data += length;
  725.             continue;
  726.         }
  727.     }
  728.     return(0);
  729. }
  730.  
  731.